{
	UCE - cross-platform game engine written in delphi.
	Copyright (c) 2009, Shutko Vitaly

	This file is part of UCE.

	UCE is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	UCE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
}

unit uPhy;

interface

uses
  uChipmunk, uTyp, uMat, uSpr;

const
  //TPhy.Bind
  PHY_POS = 1;
  PHY_ANGLE = 2;

type
  TPhy = class(TListItem)
    body: PcpBody;
    shape: PcpShape;

    p: record
      x, y, angle: PSingle;
      xoff, yoff, startAngle: Single;
      how: Integer;
    end;

    procedure Bind(spr: TSpr; how: Integer = PHY_POS or PHY_ANGLE; xoff: Single = 0; yoff: Single = 0; startAngle: Single = 0);
    procedure Update(stepSize: Cardinal);

    constructor Rect(name: String; m, i, w, h: Single);
    constructor Circle(name: String; m, i, r: Single);
    constructor Poly(name: String; m, i: Single; v: TVecs);
  end;

  TPhyJnt = class(TListItem)
    p, p2: TPhy;
    c: PcpConstraint;

    constructor DampedSpring(name: String; p, p2: TPhy; a, a2: TVec; restLength, stiffness, damping: Single);
    constructor Pin(name: String; p, p2: TPhy; a, a2: TVec); overload;
    constructor Pin(name: String; p, p2: TPhy; pv: TVec); overload;
    constructor Slide(name: String; p, p2: TPhy; a, a2: TVec; min, max: Single);
    constructor Pivot(name: String; p, p2: TPhy; a, a2: TVec); overload;
    constructor Pivot(name: String; p, p2: TPhy; pv: TVec); overload;
    constructor Groove(name: String; p, p2: TPhy; groove_a, groove_b, a2: TVec);
    constructor SimpleMotor(name: String; p, p2: TPhy; rate: Single);
    constructor Gear(name: String; p, p2: TPhy; phase, ratio: Single);
    constructor RotaryLimit(name: String; p, p2: TPhy; min, max: Single);
    constructor DampedRotarySpring(name: String; p, p2: TPhy; restAngle, stiffness, damping: Single);
  end;

  TPhySpa = class(TList)
    space: PcpSpace;
    joints: TList;

    procedure Update(stepSize: Cardinal);

    function AddPhy(p: TPhy): TPhy;
    function FindPhy(name: String): TPhy;
    procedure RemPhy(p: TPhy);

    function AddJnt(p: TPhyJnt): TPhyJnt;
    function FindJnt(name: String): TPhyJnt;
    procedure RemJnt(p: TPhyJnt);

    constructor Create;
    destructor Destroy; override;
  end;

  TPhyMan = class(TList)
    space: TPhySpa;

    function Init: Boolean;
    procedure Update(stepSize: Cardinal);

    function Add(name: String): TPhySpa;
    function Find(name: String): TPhySpa;
    procedure Rem(p: TPhySpa);

    constructor Create;
    destructor Destroy; override;
  end;

implementation

//////////////////////////////////////////////////////////////////////////////
///  TPhy
//////////////////////////////////////////////////////////////////////////////

constructor TPhy.Rect(name: String; m, i, w, h: Single);
var
  v: TVecs;
begin
  body := cpBodyNew(m, i);

  SetLength(v, 4{5});

  v[0] := MakeVec(0, 0);
  v[1] := MakeVec(w, 0);
  v[2] := MakeVec(w, h);
  v[3] := MakeVec(0, h);
  {v[4] := MakeVec(0, 0);}

  shape := cpPolyShapeNew(body, 4{5}, @v[0], cpVZero);

  Self.name := name;

  FillChar(p, SizeOf(p), 0);
end;

constructor TPhy.Circle(name: String; m, i, r: Single);
begin
  body := cpBodyNew(m, i);
  shape := cpCircleShapeNew(body, r, cpVZero);

  Self.name := name;

  FillChar(p, SizeOf(p), 0);
end;

constructor TPhy.Poly(name: String; m, i: Single; v: TVecs);
begin
  body := cpBodyNew(m, i);
  shape := cpPolyShapeNew(body, 4{5}, @v[0], cpVZero);

  Self.name := name;

  FillChar(p, SizeOf(p), 0);
end;

procedure TPhy.Bind(spr: TSpr; how: Integer = PHY_POS or PHY_ANGLE; xoff: Single = 0; yoff: Single = 0; startAngle: Single = 0);
begin
  if how and PHY_POS <> 0 then
  begin
    p.x := @spr.x;
    p.y := @spr.y;
  end else
  begin
    p.x := nil;
    p.y := nil;
  end;
  
  if how and PHY_ANGLE <> 0 then p.angle := @spr.angle else p.angle := nil;

  Self.p.xoff := xoff;
  Self.p.yoff := yoff;
  Self.p.startAngle := startAngle;
  Self.p.how := how;
end;

procedure TPhy.Update(stepSize: Cardinal);
begin
  if p.x <> nil then p.x^ := body.p.x + p.xoff;
  if p.y <> nil then p.y^ := body.p.y + p.yoff;
  if p.angle <> nil then p.angle^ := body.a + p.startAngle;
end;

//////////////////////////////////////////////////////////////////////////////
///  TPhyJnt
//////////////////////////////////////////////////////////////////////////////

function MakeCPV(v: TVec): cpVect;
begin
  Result.x := v.x;
  Result.y := v.y;
end;

constructor TPhyJnt.DampedSpring(name: String; p, p2: TPhy; a, a2: TVec; restLength, stiffness, damping: Single);
begin
  c := cpDampedSpringNew(p.body, p2.body, MakeCPV(a), MakeCPV(a2), restLength, stiffness, damping);

  Self.name := name;
end;

constructor TPhyJnt.Pin(name: String; p, p2: TPhy; a, a2: TVec);
begin
  c := cpPinJointNew(p.body, p2.body, MakeCPV(a), MakeCPV(a2));

  Self.name := name;
end;

constructor TPhyJnt.Pin(name: String; p, p2: TPhy; pv: TVec);
begin
  c := cpPinJointNew(p.body, p2.body, MakeCPV(pv));

  Self.name := name;
end;

constructor TPhyJnt.Slide(name: String; p, p2: TPhy; a, a2: TVec; min, max: Single);
begin
  c := cpSlideJointNew(p.body, p2.body, MakeCPV(a), MakeCPV(a2), min, max);

  Self.name := name;
end;

constructor TPhyJnt.Pivot(name: String; p, p2: TPhy; a, a2: TVec);
begin
  c := cpPivotJointNew(p.body, p2.body, MakeCPV(a), MakeCPV(a2));

  Self.name := name;
end;

constructor TPhyJnt.Pivot(name: String; p, p2: TPhy; pv: TVec);
begin
  c := cpPinJointNew(p.body, p2.body, MakeCPV(pv));

  Self.name := name;
end;

constructor TPhyJnt.Groove(name: String; p, p2: TPhy; groove_a, groove_b, a2: TVec);
begin
  c := cpGrooveJointNew(p.body, p2.body, MakeCPV(groove_a), MakeCPV(groove_b), MakeCPV(a2));

  Self.name := name;
end;

constructor TPhyJnt.SimpleMotor(name: String; p, p2: TPhy; rate: Single);
begin
  c := cpSimpleMotorNew(p.body, p2.body, rate);

  Self.name := name;
end;


constructor TPhyJnt.Gear(name: String; p, p2: TPhy; phase, ratio: Single);
begin
  c := cpGearJointNew(p.body, p2.body, phase, ratio);

  Self.name := name;
end;

constructor TPhyJnt.RotaryLimit(name: String; p, p2: TPhy; min, max: Single);
begin
  c := cpRotaryLimitJointNew(p.body, p2.body, min, max);

  Self.name := name;
end;

constructor TPhyJnt.DampedRotarySpring(name: String; p, p2: TPhy; restAngle, stiffness, damping: Single);
begin
  c := cpDampedRotarySpringNew(p.body, p2.body, restAngle, stiffness, damping);

  Self.name := name;
end;

//////////////////////////////////////////////////////////////////////////////
///  TPhySpa
//////////////////////////////////////////////////////////////////////////////

constructor TPhySpa.Create;
begin
  space := cpSpaceNew;
  joints := TList.Create;
end;

destructor TPhySpa.Destroy;
begin
  joints.Free_;
  joints.Free;
  
  cpSpaceFree(space);
end;

procedure TPhySpa.Update(stepSize: Cardinal);
var
  i: Integer;
begin
  cpSpaceStep(space, stepSize);

  for i := 0 to size - 1 do
    if items[i] <> nil then TPhy(items[i]).Update(stepSize);
end;

function TPhySpa.AddPhy(p: TPhy): TPhy;
begin
  Result := TPhy(inherited Add(p));
end;

function TPhySpa.FindPhy(name: String): TPhy;
begin
  Result := TPhy(inherited Find(name));
end;

procedure TPhySpa.RemPhy(p: TPhy);
begin
  inherited Rem(p);
end;

function TPhySpa.AddJnt(p: TPhyJnt): TPhyJnt;
begin
  Result := TPhyJnt(joints.Add(p));
end;

function TPhySpa.FindJnt(name: String): TPhyJnt;
begin
  Result := TPhyJnt(joints.Find(name));
end;

procedure TPhySpa.RemJnt(p: TPhyJnt);
begin
  if p <> nil then
  begin
    joints.Rem(p);
    p.Free;
  end;
end;

//////////////////////////////////////////////////////////////////////////////
///  TPhyMan
//////////////////////////////////////////////////////////////////////////////

constructor TPhyMan.Create;
begin
  space := nil;
end;

destructor TPhyMan.Destroy;
var
  i: Integer;
begin
  for i := 0 to size - 1 do
    if items[i] <> nil then TPhySpa(items[i]).Free;

  if space <> nil then
    space.Free;

  cpShutdownChipmunk;
end;

function TPhyMan.Init: Boolean;
begin
  Result := False;

  cpInitChipmunk;

  space := TPhySpa.Create;

  Result := True;
end;

procedure TPhyMan.Update(stepSize: Cardinal);
var
  i: Integer;
begin
  space.Update(stepSize);

  for i := 0 to size - 1 do
    if items[i] <> nil then TPhySpa(items[i]).Update(stepSize);
end;

function TPhyMan.Add(name: String): TPhySpa;
begin
  Result := TPhySpa.Create;
  Result.name := name;

  inherited Add(Result);
end;

function TPhyMan.Find(name: String): TPhySpa;
begin
  Result := TPhySpa(inherited Find(name));
end;

procedure TPhyMan.Rem(p: TPhySpa);
begin
  if p <> nil then
  begin
    inherited Rem(p);
    p.Free;
  end;
end;

end.
